//! HTTP request and response models
//! 
//! This module defines HTTP-related data structures,
//! corresponding to HttpRunner's Request and Response models.

use crate::models::{Value, ValueMap, StringMap};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::time::Duration;

/// HTTP methods (corresponds to HttpRunner's HTTPMethod)
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum HttpMethod {
    GET,
    HEAD,
    POST,
    PUT,
    DELETE,
    OPTIONS,
    PATCH,
    TRACE,
    CONNECT,
}

/// HTTP request structure (corresponds to HttpRunner's Request)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HttpRequest {
    /// HTTP method
    pub method: HttpMethod,
    
    /// Request URL
    pub url: String,
    
    /// Use HTTP/2
    #[serde(default)]
    pub http2: bool,
    
    /// Query parameters
    #[serde(default)]
    pub params: ValueMap,
    
    /// Request headers
    #[serde(default)]
    pub headers: StringMap,
    
    /// Request cookies
    #[serde(default)]
    pub cookies: StringMap,
    
    /// Request body (raw)
    pub body: Option<Value>,
    
    /// JSON body
    pub json: Option<Value>,
    
    /// Form data
    pub data: Option<Value>,
    
    /// Request timeout in seconds
    pub timeout: Option<f64>,
    
    /// Allow redirects
    #[serde(default = "default_allow_redirects")]
    pub allow_redirects: bool,
    
    /// Verify SSL certificates
    #[serde(default)]
    pub verify: bool,
    
    /// File upload configuration
    #[serde(default)]
    pub upload: ValueMap,
    
    /// Proxy configuration
    pub proxy: Option<String>,
    
    /// Authentication
    pub auth: Option<AuthConfig>,
}

/// Authentication configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuthConfig {
    /// Authentication type
    pub auth_type: AuthType,
    
    /// Username (for basic auth)
    pub username: Option<String>,
    
    /// Password (for basic auth)
    pub password: Option<String>,
    
    /// Token (for bearer auth)
    pub token: Option<String>,
    
    /// API key
    pub api_key: Option<String>,
    
    /// Additional auth parameters
    #[serde(default)]
    pub params: ValueMap,
}

/// Authentication types
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum AuthType {
    Basic,
    Bearer,
    ApiKey,
    OAuth1,
    OAuth2,
    Custom(String),
}

/// HTTP response structure
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HttpResponse {
    /// Response status code
    pub status_code: u16,
    
    /// Response reason phrase
    pub reason: String,
    
    /// Response headers
    pub headers: StringMap,
    
    /// Response cookies
    pub cookies: StringMap,
    
    /// Response body as text
    pub text: String,
    
    /// Response body as bytes
    #[serde(skip)]
    pub content: Vec<u8>,
    
    /// Response body as JSON (if applicable)
    pub json: Option<Value>,
    
    /// Response encoding
    pub encoding: String,
    
    /// Response URL (after redirects)
    pub url: String,
    
    /// Request duration
    pub elapsed: Duration,
    
    /// Response size in bytes
    pub content_length: Option<u64>,
    
    /// Content type
    pub content_type: Option<String>,
    
    /// HTTP version
    pub version: HttpVersion,
    
    /// Whether the request was successful (2xx status)
    pub ok: bool,
    
    /// Request history (redirects)
    pub history: Vec<HttpResponse>,
}

/// Get HTTP status reason phrase
pub fn status_reason(status_code: u16) -> String {
    match status_code {
        100 => "Continue".to_string(),
        101 => "Switching Protocols".to_string(),
        200 => "OK".to_string(),
        201 => "Created".to_string(),
        202 => "Accepted".to_string(),
        204 => "No Content".to_string(),
        301 => "Moved Permanently".to_string(),
        302 => "Found".to_string(),
        304 => "Not Modified".to_string(),
        400 => "Bad Request".to_string(),
        401 => "Unauthorized".to_string(),
        403 => "Forbidden".to_string(),
        404 => "Not Found".to_string(),
        405 => "Method Not Allowed".to_string(),
        409 => "Conflict".to_string(),
        422 => "Unprocessable Entity".to_string(),
        429 => "Too Many Requests".to_string(),
        500 => "Internal Server Error".to_string(),
        502 => "Bad Gateway".to_string(),
        503 => "Service Unavailable".to_string(),
        504 => "Gateway Timeout".to_string(),
        _ => format!("Status {}", status_code),
    }
}

/// HTTP version
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum HttpVersion {
    Http09,
    Http10,
    Http11,
    Http2,
    Http3,
}

/// Request builder for constructing HTTP requests
#[derive(Debug, Clone)]
pub struct RequestBuilder {
    request: HttpRequest,
}

impl HttpRequest {
    /// Create a new HTTP request
    pub fn new(method: HttpMethod, url: String) -> Self {
        Self {
            method,
            url,
            http2: false,
            params: HashMap::new(),
            headers: HashMap::new(),
            cookies: HashMap::new(),
            body: None,
            json: None,
            data: None,
            timeout: None,
            allow_redirects: default_allow_redirects(),
            verify: false,
            upload: HashMap::new(),
            proxy: None,
            auth: None,
        }
    }
    
    /// Create a GET request
    pub fn get(url: String) -> Self {
        Self::new(HttpMethod::GET, url)
    }
    
    /// Create a POST request
    pub fn post(url: String) -> Self {
        Self::new(HttpMethod::POST, url)
    }
    
    /// Create a PUT request
    pub fn put(url: String) -> Self {
        Self::new(HttpMethod::PUT, url)
    }
    
    /// Create a DELETE request
    pub fn delete(url: String) -> Self {
        Self::new(HttpMethod::DELETE, url)
    }
    
    /// Set request headers
    pub fn with_headers(mut self, headers: StringMap) -> Self {
        self.headers = headers;
        self
    }
    
    /// Add a header
    pub fn header(mut self, key: String, value: String) -> Self {
        self.headers.insert(key, value);
        self
    }
    
    /// Set query parameters
    pub fn with_params(mut self, params: ValueMap) -> Self {
        self.params = params;
        self
    }
    
    /// Add a query parameter
    pub fn param(mut self, key: String, value: Value) -> Self {
        self.params.insert(key, value);
        self
    }
    
    /// Set JSON body
    pub fn json(mut self, json: Value) -> Self {
        self.json = Some(json);
        self.headers.insert("Content-Type".to_string(), "application/json".to_string());
        self
    }
    
    /// Set form data
    pub fn form(mut self, data: Value) -> Self {
        self.data = Some(data);
        self.headers.insert("Content-Type".to_string(), "application/x-www-form-urlencoded".to_string());
        self
    }
    
    /// Set raw body
    pub fn body(mut self, body: Value) -> Self {
        self.body = Some(body);
        self
    }
    
    /// Set timeout
    pub fn timeout(mut self, timeout: f64) -> Self {
        self.timeout = Some(timeout);
        self
    }
    
    /// Set authentication
    pub fn auth(mut self, auth: AuthConfig) -> Self {
        self.auth = Some(auth);
        self
    }
    
    /// Set basic authentication
    pub fn basic_auth(mut self, username: String, password: String) -> Self {
        self.auth = Some(AuthConfig {
            auth_type: AuthType::Basic,
            username: Some(username),
            password: Some(password),
            token: None,
            api_key: None,
            params: HashMap::new(),
        });
        self
    }
    
    /// Set bearer token authentication
    pub fn bearer_auth(mut self, token: String) -> Self {
        self.auth = Some(AuthConfig {
            auth_type: AuthType::Bearer,
            username: None,
            password: None,
            token: Some(token),
            api_key: None,
            params: HashMap::new(),
        });
        self
    }
    
    /// Enable SSL verification
    pub fn verify_ssl(mut self, verify: bool) -> Self {
        self.verify = verify;
        self
    }
    
    /// Enable HTTP/2
    pub fn http2(mut self, enable: bool) -> Self {
        self.http2 = enable;
        self
    }
}

impl HttpResponse {
    /// Create a new HTTP response
    pub fn new(status_code: u16, text: String) -> Self {
        Self {
            status_code,
            reason: status_reason(status_code),
            headers: HashMap::new(),
            cookies: HashMap::new(),
            text,
            content: Vec::new(),
            json: None,
            encoding: "utf-8".to_string(),
            url: String::new(),
            elapsed: Duration::from_secs(0),
            content_length: None,
            content_type: None,
            version: HttpVersion::Http11,
            ok: (200..300).contains(&status_code),
            history: Vec::new(),
        }
    }
    
    /// Check if response is successful (2xx)
    pub fn is_success(&self) -> bool {
        self.ok
    }
    
    /// Check if response is client error (4xx)
    pub fn is_client_error(&self) -> bool {
        (400..500).contains(&self.status_code)
    }
    
    /// Check if response is server error (5xx)
    pub fn is_server_error(&self) -> bool {
        (500..600).contains(&self.status_code)
    }
    
    /// Get response as JSON
    pub fn json_value(&self) -> Option<&Value> {
        self.json.as_ref()
    }
    
    /// Get header value
    pub fn header(&self, name: &str) -> Option<&String> {
        self.headers.get(name)
    }
    
    /// Get cookie value
    pub fn cookie(&self, name: &str) -> Option<&String> {
        self.cookies.get(name)
    }
}

impl RequestBuilder {
    /// Create a new request builder
    pub fn new(method: HttpMethod, url: String) -> Self {
        Self {
            request: HttpRequest::new(method, url),
        }
    }
    
    /// Build the request
    pub fn build(self) -> HttpRequest {
        self.request
    }
}

impl std::fmt::Display for HttpMethod {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            HttpMethod::GET => write!(f, "GET"),
            HttpMethod::HEAD => write!(f, "HEAD"),
            HttpMethod::POST => write!(f, "POST"),
            HttpMethod::PUT => write!(f, "PUT"),
            HttpMethod::DELETE => write!(f, "DELETE"),
            HttpMethod::OPTIONS => write!(f, "OPTIONS"),
            HttpMethod::PATCH => write!(f, "PATCH"),
            HttpMethod::TRACE => write!(f, "TRACE"),
            HttpMethod::CONNECT => write!(f, "CONNECT"),
        }
    }
}

impl std::str::FromStr for HttpMethod {
    type Err = String;
    
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s.to_uppercase().as_str() {
            "GET" => Ok(HttpMethod::GET),
            "HEAD" => Ok(HttpMethod::HEAD),
            "POST" => Ok(HttpMethod::POST),
            "PUT" => Ok(HttpMethod::PUT),
            "DELETE" => Ok(HttpMethod::DELETE),
            "OPTIONS" => Ok(HttpMethod::OPTIONS),
            "PATCH" => Ok(HttpMethod::PATCH),
            "TRACE" => Ok(HttpMethod::TRACE),
            "CONNECT" => Ok(HttpMethod::CONNECT),
            _ => Err(format!("Invalid HTTP method: {}", s)),
        }
    }
}

// Helper functions
fn default_allow_redirects() -> bool {
    true
}



#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_http_request_creation() {
        let request = HttpRequest::get("https://api.example.com/users".to_string());
        assert_eq!(request.method, HttpMethod::GET);
        assert_eq!(request.url, "https://api.example.com/users");
        assert!(request.allow_redirects);
    }

    #[test]
    fn test_http_request_builder() {
        let request = HttpRequest::post("https://api.example.com/users".to_string())
            .json(serde_json::json!({"name": "John"}))
            .header("Authorization".to_string(), "Bearer token123".to_string())
            .timeout(30.0);
        
        assert_eq!(request.method, HttpMethod::POST);
        assert!(request.json.is_some());
        assert_eq!(request.headers.get("Authorization"), Some(&"Bearer token123".to_string()));
        assert_eq!(request.timeout, Some(30.0));
    }

    #[test]
    fn test_http_response() {
        let response = HttpResponse::new(200, "OK".to_string());
        assert_eq!(response.status_code, 200);
        assert!(response.is_success());
        assert!(!response.is_client_error());
        assert!(!response.is_server_error());
    }

    #[test]
    fn test_http_method_from_str() {
        assert_eq!("GET".parse::<HttpMethod>().unwrap(), HttpMethod::GET);
        assert_eq!("post".parse::<HttpMethod>().unwrap(), HttpMethod::POST);
        assert!("INVALID".parse::<HttpMethod>().is_err());
    }
}
